package com.libraries.helper;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;

import com.facebook.cache.disk.DiskCacheConfig;
import com.facebook.common.internal.Supplier;
import com.facebook.common.logging.FLog;
import com.facebook.common.util.ByteConstants;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.controller.ControllerListener;
import com.facebook.drawee.drawable.ProgressBarDrawable;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.interfaces.SimpleDraweeControllerBuilder;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.cache.MemoryCacheParams;
import com.facebook.imagepipeline.common.ImageDecodeOptions;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequest.ImageType;
import com.facebook.imagepipeline.request.ImageRequest.RequestLevel;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
import com.nostra13.universalimageloader.cache.memory.impl.UsingFreqLimitedMemoryCache;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.widget.ImageView;

public class ImgHelper {
	private static final String TAG = "ImgHelper";

	private static final int MAX_HEAP_SIZE = (int) Runtime.getRuntime()
																			.maxMemory();// 分配的可用内存
	private static final int MAX_MEMORY_CACHE_SIZE = MAX_HEAP_SIZE / 4;// 使用的缓存数量

	private static final int MAX_SMALL_DISK_VERYLOW_CACHE_SIZE = 5 * ByteConstants.MB;// 小图极低磁盘空间缓存的最大值（特性：可将大量的小图放到额外放在另一个磁盘空间防止大图占用磁盘空间而删除了大量的小图）
	private static final int MAX_SMALL_DISK_LOW_CACHE_SIZE = 10 * ByteConstants.MB;// 小图低磁盘空间缓存的最大值（特性：可将大量的小图放到额外放在另一个磁盘空间防止大图占用磁盘空间而删除了大量的小图）
	private static final int MAX_SMALL_DISK_CACHE_SIZE = 20 * ByteConstants.MB;// 小图磁盘缓存的最大值（特性：可将大量的小图放到额外放在另一个磁盘空间防止大图占用磁盘空间而删除了大量的小图）

	private static final int MAX_DISK_CACHE_VERYLOW_SIZE = 10 * ByteConstants.MB;// 默认图极低磁盘空间缓存的最大值
	private static final int MAX_DISK_CACHE_LOW_SIZE = 30 * ByteConstants.MB;// 默认图低磁盘空间缓存的最大值
	private static final int MAX_DISK_CACHE_SIZE = 50 * ByteConstants.MB;// 默认图磁盘缓存的最大值
	
	public static final String IMAGE_CONTENT_URI = "content://";// asset目录下的资源
	public static final String IMAGE_HTTPS_URI = "https://";// 远程图片
	public static final String IMAGE_HTTP_URI = "http://";// 远程图片
	public static final String IMAGE_ASSET_URI = "asset://";// asset目录下的资源
	public static final String IMAGE_FILE_URI = "file://";// 本地文件
	public static final String IMAGE_RES_URI = "res:///";// res目录下的资源

	private static String cacheFilePath;

	public static void initImgLoader(Context context) {
		Logger.log(TAG, "initImgLoader", DeviceHelper.CURR_WIDTH,
				DeviceHelper.CURR_HEIGHT);
		cacheFilePath = FileHelper.getDiskCachePath(FileHelper.IMAGE_CACHE_DIR);
		ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(
				context)
				.memoryCacheExtraOptions(DeviceHelper.CURR_WIDTH,
						DeviceHelper.CURR_HEIGHT)
				// max width, max height，即保存的每个缓存文件的最大长宽
				.threadPoolSize(5)
				// 线程池内加载的数量
				.threadPriority(Thread.NORM_PRIORITY - 2)
				.denyCacheImageMultipleSizesInMemory()
				.memoryCache(new UsingFreqLimitedMemoryCache(5 * 1024 * 1024))
				// You can pass your own memory cache
				// implementation/你可以通过自己的内存缓存实现
				.memoryCacheSize(10 * 1024 * 1024)
				.diskCacheSize(50 * 1024 * 1024)
				.diskCache(new UnlimitedDiskCache(new File(cacheFilePath)))
				// 自定义缓存路径
				.diskCacheFileNameGenerator(new Md5FileNameGenerator())
				// 将保存的时候的URI名称用MD5 加密
				.tasksProcessingOrder(QueueProcessingType.LIFO)
				.imageDownloader(
						new BaseImageDownloader(context, 5 * 1000, 30 * 1000)) // connectTimeout//
																				// (5s,readTimeout,30s)超时时间
				.writeDebugLogs() // Remove for release app
				.build();// 开始构建
		ImageLoader.getInstance().init(configuration);
	}

	private static DisplayImageOptions getDisplayOptions(boolean cacheDisk,
			int defaultRes) {
		DisplayImageOptions options = new DisplayImageOptions.Builder()
				.showImageOnLoading(defaultRes) // 设置图片在下载期间显示的图片
				.showImageForEmptyUri(defaultRes)// 设置图片Uri为空或是错误的时候显示的图片
				.showImageOnFail(defaultRes) // 设置图片加载/解码过程中错误时候显示的图片
				.cacheInMemory(true)// 设置下载的图片是否缓存在内存中
				.cacheOnDisk(cacheDisk)// 设置下载的图片是否缓存在SD卡中
				.considerExifParams(true) // 是否考虑JPEG图像EXIF参数（旋转，翻转）
				.imageScaleType(ImageScaleType.EXACTLY_STRETCHED)// 缩放类型imageScaleType:
				// EXACTLY :图像将完全按比例缩小的目标大小
				// EXACTLY_STRETCHED:图片会缩放到目标大小完全
				// IN_SAMPLE_INT:图像将被二次采样的整数倍
				// IN_SAMPLE_POWER_OF_2:图片将降低2倍，直到下一减少步骤，使图像更小的目标大小
				// NONE:图片不会调整
				.bitmapConfig(Bitmap.Config.RGB_565)// 设置图片的解码类型//
				// .decodingOptions(BitmapFactory.Options
				// decodingOptions)//设置图片的解码配置
				// .delayBeforeLoading(int delayInMillis)//int
				// delayInMillis为你设置的下载前的延迟时间
				// 设置图片加入缓存前，对bitmap进行设置
				// .preProcessor(BitmapProcessor preProcessor)
				.resetViewBeforeLoading(true)// 设置图片在下载前是否重置，复位
				.displayer(new RoundedBitmapDisplayer(20))// 是否设置为圆角，弧度为多少
				.displayer(new FadeInBitmapDisplayer(100))// 是否图片加载好后渐入的动画时间
				.build();// 构建完成
		return options;
	}

	public static void initFrescoLoader(Context context) {
		FLog.setMinimumLoggingLevel(FLog.VERBOSE);// 日志打印等级
		Fresco.initialize(context, configureCaches(context));// 图片缓存初始化配置
	}

	/**
	 * 初始化配置
	 */
	private static ImagePipelineConfig configureCaches(Context context) {
		cacheFilePath = FileHelper.getDiskCachePath(FileHelper.IMAGE_CACHE_DIR);
		// 内存配置
		final MemoryCacheParams bitmapCacheParams = new MemoryCacheParams(
				MAX_MEMORY_CACHE_SIZE, // 内存缓存中总图片的最大大小,以字节为单位。
				Integer.MAX_VALUE, // 内存缓存中图片的最大数量。
				MAX_MEMORY_CACHE_SIZE, // 内存缓存中准备清除但尚未被删除的总图片的最大大小,以字节为单位。
				Integer.MAX_VALUE, // 内存缓存中准备清除的总图片的最大数量。
				Integer.MAX_VALUE); // 内存缓存中单个图片的最大大小。

		// 修改内存图片缓存数量，空间策略（这个方式有点恶心）
		Supplier<MemoryCacheParams> mSupplierMemoryCacheParams = new Supplier<MemoryCacheParams>() {
			@Override
			public MemoryCacheParams get() {
				return bitmapCacheParams;
			}
		};

		// 小图片的磁盘配置
		DiskCacheConfig diskSmallCacheConfig = DiskCacheConfig.newBuilder(context)
				.setBaseDirectoryPath(new File(FileHelper.getDiskPath()))// 缓存图片基路径
				.setBaseDirectoryName(FileHelper.IMAGE_SMALL_CACHE_DIR)// 文件夹名
				// .setCacheErrorLogger(cacheErrorLogger)//日志记录器用于日志错误的缓存。
				// .setCacheEventListener(cacheEventListener)//缓存事件侦听器。
				// .setDiskTrimmableRegistry(diskTrimmableRegistry)//类将包含一个注册表的缓存减少磁盘空间的环境。
				.setMaxCacheSize(MAX_SMALL_DISK_CACHE_SIZE)// 默认缓存的最大大小。
				.setMaxCacheSizeOnLowDiskSpace(MAX_SMALL_DISK_LOW_CACHE_SIZE)// 缓存的最大大小,使用设备时低磁盘空间。
				.setMaxCacheSizeOnVeryLowDiskSpace(
						MAX_SMALL_DISK_VERYLOW_CACHE_SIZE)// 缓存的最大大小,当设备极低磁盘空间
				// .setVersion(version)
				.build();

		// 默认图片的磁盘配置
		DiskCacheConfig diskCacheConfig = DiskCacheConfig.newBuilder(context)
				.setBaseDirectoryPath(new File(FileHelper.getDiskPath()))// 缓存图片基路径
				.setBaseDirectoryName(FileHelper.IMAGE_CACHE_DIR)// 文件夹名
				// .setCacheErrorLogger(cacheErrorLogger)//日志记录器用于日志错误的缓存。
				// .setCacheEventListener(cacheEventListener)//缓存事件侦听器。
				// .setDiskTrimmableRegistry(diskTrimmableRegistry)//类将包含一个注册表的缓存减少磁盘空间的环境。
				.setMaxCacheSize(MAX_DISK_CACHE_SIZE)// 默认缓存的最大大小。
				.setMaxCacheSizeOnLowDiskSpace(MAX_DISK_CACHE_LOW_SIZE)// 缓存的最大大小,使用设备时低磁盘空间。
				.setMaxCacheSizeOnVeryLowDiskSpace(MAX_DISK_CACHE_VERYLOW_SIZE)// 缓存的最大大小,当设备极低磁盘空间
				// .setVersion(version)
				.build();

		// 缓存图片配置
		ImagePipelineConfig.Builder configBuilder = ImagePipelineConfig
				.newBuilder(context)
				// .setAnimatedImageFactory(AnimatedImageFactory
				// animatedImageFactory)//图片加载动画
				.setBitmapMemoryCacheParamsSupplier(mSupplierMemoryCacheParams)// 内存缓存配置（一级缓存，已解码的图片）
				// .setCacheKeyFactory(cacheKeyFactory)//缓存Key工厂
				// .setEncodedMemoryCacheParamsSupplier(encodedCacheParamsSupplier)//内存缓存和未解码的内存缓存的配置（二级缓存）
				// .setExecutorSupplier(executorSupplier)//线程池配置
				// .setImageCacheStatsTracker(imageCacheStatsTracker)//统计缓存的命中率
				// .setImageDecoder(ImageDecoder imageDecoder) //图片解码器配置
				// .setIsPrefetchEnabledSupplier(Supplier<Boolean>
				// isPrefetchEnabledSupplier)//图片预览（缩略图，预加载图等）预加载到文件缓存
				.setMainDiskCacheConfig(diskCacheConfig)// 磁盘缓存配置（总，三级缓存）
				// .setMemoryTrimmableRegistry(memoryTrimmableRegistry)
				// //内存用量的缩减,有时我们可能会想缩小内存用量。比如应用中有其他数据需要占用内存，不得不把图片缓存清除或者减小
				// 或者我们想检查看看手机是否已经内存不够了。
				// .setNetworkFetchProducer(networkFetchProducer)//自定的网络层配置：如OkHttp，Volley
				// .setPoolFactory(poolFactory)//线程池工厂配置
				// .setProgressiveJpegConfig(progressiveJpegConfig)//渐进式JPEG图
				// .setRequestListeners(requestListeners)//图片请求监听
				// .setResizeAndRotateEnabledForNetwork(boolean
				// resizeAndRotateEnabledForNetwork)//调整和旋转是否支持网络图片
				.setSmallImageDiskCacheConfig(diskSmallCacheConfig)// 磁盘缓存配置（小图片，可选～三级缓存的小图优化缓存）
		;
		return configBuilder.build();
	}

	// 圆形，圆角切图，对动图无效
	public static RoundingParams getRoundingParams(float radius) {
		RoundingParams roundingParams = RoundingParams
				.fromCornersRadius(radius);
		// roundingParams.asCircle();//圆形
		// roundingParams.setBorder(color,width);
		// fresco:roundingBorderWidth="2dp"边框
		// fresco:roundingBorderColor="@color/border_color"
		// roundingParams.setCornersRadii(radii);//半径
		// roundingParams.setCornersRadii(topLeft, topRight, bottomRight,
		// bottomLeft)//fresco:roundTopLeft="true" fresco:roundTopRight="false"
		// fresco:roundBottomLeft="false" fresco:roundBottomRight="true"
		// roundingParams.setCornersRadius(radius);
		// fresco:roundedCornerRadius="1dp"圆角
		// roundingParams.setOverlayColor(overlayColor);
		// fresco:roundWithOverlayColor="@color/corner_color"
		// roundingParams.setRoundAsCircle(roundAsCircle);//圆
		// roundingParams.setRoundingMethod(roundingMethod);
		// fresco:progressBarAutoRotateInterval="1000"自动旋转间隔
		// 或用 fromCornersRadii 以及 asCircle 方法
		return roundingParams;
	}

	// Drawees DraweeHierarchy 组织
	public static GenericDraweeHierarchy getGenericDraweeHierarchy(
			Context context) {
		GenericDraweeHierarchy gdh = new GenericDraweeHierarchyBuilder(
				context.getResources())
		// .reset()//重置
		// .setActualImageColorFilter(colorFilter)//颜色过滤
		// .setActualImageFocusPoint(focusPoint)//focusCrop, 需要指定一个居中点
		// .setActualImageMatrix(actualImageMatrix)
		// .setActualImageScaleType(actualImageScaleType)//fresco:actualImageScaleType="focusCrop"缩放类型
		// .setBackground(background)//fresco:backgroundImage="@color/blue"背景图片
		// .setBackgrounds(backgrounds)
		// .setFadeDuration(fadeDuration)//fresco:fadeDuration="300"加载图片动画时间
		// .setFailureImage(ConfigConstants.sErrorDrawable)//
		// fresco:failureImage="@drawable/error"失败图
				// .setFailureImage(failureDrawable,
				// failureImageScaleType)//fresco:failureImageScaleType="centerInside"失败图缩放类型
				// .setOverlay(overlay)//fresco:overlayImage="@drawable/watermark"叠加图
				// .setOverlays(overlays)
				// .setPlaceholderImage(ConfigConstants.sPlaceholderDrawable)//
				// fresco:placeholderImage="@color/wait_color"占位图
				// .setPlaceholderImage(placeholderDrawable,
				// placeholderImageScaleType)//fresco:placeholderImageScaleType="fitCenter"占位图缩放类型
				// .setPressedStateOverlay(drawable)//fresco:pressedStateOverlayImage="@color/red"按压状态下的叠加图
				.setProgressBarImage(new ProgressBarDrawable())// 进度条fresco:progressBarImage="@drawable/progress_bar"进度条
				// .setProgressBarImage(progressBarImage,
				// progressBarImageScaleType)//fresco:progressBarImageScaleType="centerInside"进度条类型
				// .setRetryImage(retryDrawable)//fresco:retryImage="@drawable/retrying"点击重新加载
				// .setRetryImage(retryDrawable,
				// retryImageScaleType)//fresco:retryImageScaleType="centerCrop"点击重新加载缩放类型
				.setRoundingParams(RoundingParams.asCircle())// 圆形/圆角fresco:roundAsCircle="true"圆形
				.build();
		return gdh;
	}

	// SimpleDraweeControllerBuilder
	public static SimpleDraweeControllerBuilder getSimpleDraweeControllerBuilder(
			SimpleDraweeControllerBuilder sdcb, Uri uri, Object callerContext,
			DraweeController draweeController) {
		SimpleDraweeControllerBuilder controllerBuilder = sdcb.setUri(uri)
				.setCallerContext(callerContext)
				// .setAspectRatio(1.33f);//宽高缩放比
				.setOldController(draweeController);
		return controllerBuilder;
	}

	// 图片解码
	public static ImageDecodeOptions getImageDecodeOptions() {
		ImageDecodeOptions decodeOptions = ImageDecodeOptions.newBuilder()
		// .setBackgroundColor(Color.TRANSPARENT)//图片的背景颜色
		// .setDecodeAllFrames(decodeAllFrames)//解码所有帧
		// .setDecodePreviewFrame(decodePreviewFrame)//解码预览框
		// .setForceOldAnimationCode(forceOldAnimationCode)//使用以前动画
		// .setFrom(options)//使用已经存在的图像解码
		// .setMinDecodeIntervalMs(intervalMs)//最小解码间隔（分位单位）
				.setUseLastFrameForPreview(true)// 使用最后一帧进行预览
				.build();
		return decodeOptions;
	}

	// 图片显示
	public static ImageRequest getImageRequest(SimpleDraweeView view, String uri) {
		Logger.log(TAG, "getImageRequest", view.getLayoutParams().height,view.getLayoutParams().width);
		ImageRequest imageRequest = ImageRequestBuilder
				.newBuilderWithSource(Uri.parse(uri))
				// .setAutoRotateEnabled(true)//自动旋转图片方向
				// .setImageDecodeOptions(getImageDecodeOptions())//  图片解码库
				 .setImageType(ImageType.DEFAULT)//图片类型，设置后可调整图片放入小图磁盘空间还是默认图片磁盘空间
				 .setLocalThumbnailPreviewsEnabled(true)//缩略图预览，影响图片显示速度（轻微）
				.setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)
				// 请求经过缓存级别
				// BITMAP_MEMORY_CACHE，ENCODED_MEMORY_CACHE，DISK_CACHE，FULL_FETCH
				// .setPostprocessor(postprocessor)//修改图片
				// .setProgressiveRenderingEnabled(true)//渐进加载，主要用于渐进式的JPEG图，影响图片显示速度（普通）
//				.setResizeOptions(
//						new ResizeOptions(view.getWidth(), view.getHeight()))// 调整大小
				// .setSource(Uri uri)//设置图片地址
				.build();
		return imageRequest;
	}

	// DraweeController 控制 DraweeControllerBuilder
	public static DraweeController getDraweeController(
			ImageRequest imageRequest, SimpleDraweeView draweeView,
			ControllerListener<? super ImageInfo> listener) {
		DraweeController draweeController = Fresco.newDraweeControllerBuilder()
		// .reset()//重置
				.setAutoPlayAnimations(true)// 自动播放图片动画
				// .setCallerContext(callerContext)//回调
				.setControllerListener(listener)// 监听图片下载完毕等
				// .setDataSourceSupplier(dataSourceSupplier)//数据源
				// .setFirstAvailableImageRequests(firstAvailableImageRequests)//本地图片复用，可加入ImageRequest数组
				.setImageRequest(imageRequest)// 设置单个图片请求～～～不可与setFirstAvailableImageRequests共用，配合setLowResImageRequest为高分辨率的图
				// .setLowResImageRequest(ImageRequest.fromUri(lowResUri))//先下载显示低分辨率的图
				.setOldController(draweeView.getController())// DraweeController复用
				.setTapToRetryEnabled(true)// 点击重新加载图
				.build();
		draweeView.setController(draweeController);
		return draweeController;
	}
	
	// DraweeController 控制 DraweeControllerBuilder
	public static DraweeController getZoomableDraweeController(
			ImageRequest imageRequest, SimpleDraweeView draweeView,
			ControllerListener<? super ImageInfo> listener) {
		DraweeController draweeController = Fresco.newDraweeControllerBuilder()
		// .reset()//重置
				.setAutoPlayAnimations(true)// 自动播放图片动画
				// .setCallerContext(callerContext)//回调
				.setControllerListener(listener)// 监听图片下载完毕等
				// .setDataSourceSupplier(dataSourceSupplier)//数据源
				// .setFirstAvailableImageRequests(firstAvailableImageRequests)//本地图片复用，可加入ImageRequest数组
				.setImageRequest(imageRequest)// 设置单个图片请求～～～不可与setFirstAvailableImageRequests共用，配合setLowResImageRequest为高分辨率的图
				// .setLowResImageRequest(ImageRequest.fromUri(lowResUri))//先下载显示低分辨率的图
				.setOldController(draweeView.getController())// DraweeController复用
				.setTapToRetryEnabled(true)// 点击重新加载图
				.build();
		draweeView.setController(draweeController);
		return draweeController;
	}
	
	public static GenericDraweeHierarchy  loadFileByFresco(SimpleDraweeView draweeView,String filePath,ControllerListener<? super ImageInfo> controllerListener){
		String fileUri=IMAGE_FILE_URI+filePath;
		ImageRequest imageRequest = getImageRequest(draweeView,fileUri);
		getDraweeController(imageRequest, draweeView,controllerListener);
		return draweeView.getHierarchy();
	}
	
	public static GenericDraweeHierarchy  loadResByFresco(SimpleDraweeView draweeView,int resId,ControllerListener<? super ImageInfo> controllerListener){
		String resUri=IMAGE_RES_URI+resId;
		ImageRequest imageRequest = getImageRequest(draweeView,resUri);
		getDraweeController(imageRequest, draweeView,controllerListener);
		return draweeView.getHierarchy();
	}
	
	public static GenericDraweeHierarchy  loadAssetByFresco(SimpleDraweeView draweeView,String asset,ControllerListener<? super ImageInfo> controllerListener){
		String assetUri=IMAGE_ASSET_URI+asset;
		ImageRequest imageRequest = getImageRequest(draweeView,assetUri);
		getDraweeController(imageRequest, draweeView,controllerListener);
		return draweeView.getHierarchy();
	}
	
	public static GenericDraweeHierarchy  loadLinkByFresco(SimpleDraweeView draweeView,String imgLink,ControllerListener<? super ImageInfo> controllerListener){
//		Logger.log(TAG, "loadImgByLink", imgLink);
		ImageRequest imageRequest = getImageRequest(draweeView,imgLink);
		getDraweeController(imageRequest, draweeView,controllerListener);
		return draweeView.getHierarchy();
	}

	
	
	// 从资源中获取Bitmap
	public static Bitmap getBitmap(Context context, int rid) {
		Resources res = context.getResources();
		return BitmapFactory.decodeResource(res, rid);
	}

	/**
	 * 根据原图和变长绘制圆形图片
	 * 
	 * @param source
	 * @param min
	 * @return
	 */
	public static Bitmap getRoundBitmap(Bitmap source, int min) {
		final Paint paint = new Paint();
		paint.setAntiAlias(true);
		Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888);
		/**
		 * 产生一个同样大小的画布
		 */
		Canvas canvas = new Canvas(target);
		/**
		 * 首先绘制圆形
		 */
		canvas.drawCircle(min / 2, min / 2, min / 2, paint);
		/**
		 * 使用SRC_IN，参考上面的说明
		 */
		paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
		/**
		 * 绘制图片
		 */
		canvas.drawBitmap(source, 0, 0, paint);
		return target;
	}

	/**
	 * 获得圆角图片
	 * 
	 * @param bitmap
	 * @param roundPx
	 * @return
	 */
	public static Bitmap getRoundCornerBitmap(Bitmap bitmap, float roundPx) {
		int w = bitmap.getWidth();
		int h = bitmap.getHeight();
		Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
		Canvas canvas = new Canvas(output);
		final int color = 0xff424242;
		final Paint paint = new Paint();
		final Rect rect = new Rect(0, 0, w, h);
		final RectF rectF = new RectF(rect);
		paint.setAntiAlias(true);
		canvas.drawARGB(0, 0, 0, 0);
		paint.setColor(color);
		canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
		paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
		canvas.drawBitmap(bitmap, rect, rect, paint);
		return output;
	}

	/**
	 * 获得带倒影的图片
	 * 
	 * @param bitmap
	 * @return
	 */
	public static Bitmap createReflectionImageWithOrigin(Bitmap bitmap) {
		final int reflectionGap = 4;
		int w = bitmap.getWidth();
		int h = bitmap.getHeight();

		Matrix matrix = new Matrix();
		matrix.preScale(1, -1);

		Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, h / 2, w,
				h / 2, matrix, false);

		Bitmap bitmapWithReflection = Bitmap.createBitmap(w, (h + h / 2),
				Config.ARGB_8888);

		Canvas canvas = new Canvas(bitmapWithReflection);
		canvas.drawBitmap(bitmap, 0, 0, null);
		Paint deafalutPaint = new Paint();
		canvas.drawRect(0, h, w, h + reflectionGap, deafalutPaint);

		canvas.drawBitmap(reflectionImage, 0, h + reflectionGap, null);

		Paint paint = new Paint();
		LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0,
				bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff,
				0x00ffffff, TileMode.CLAMP);
		paint.setShader(shader);
		// Set the Transfer mode to be porter duff and destination in
		paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
		// Draw a rectangle using the paint with our linear gradient
		canvas.drawRect(0, h, w, bitmapWithReflection.getHeight()
				+ reflectionGap, paint);

		return bitmapWithReflection;
	}

	/**
	 * 缩放
	 * 
	 * @param drawable
	 * @param w
	 * @param h
	 * @return
	 */
	@SuppressWarnings("deprecation")
	public static Drawable zoomDrawable(Drawable drawable, int w, int h) {
		int width = drawable.getIntrinsicWidth();
		int height = drawable.getIntrinsicHeight();
		// drawable转换成bitmap
		Bitmap oldbmp = drawableToBitmap(drawable);
		// 创建操作图片用的Matrix对象
		Matrix matrix = new Matrix();
		// 计算缩放比例
		float sx = ((float) w / width);
		float sy = ((float) h / height);
		// 设置缩放比例
		matrix.postScale(sx, sy);
		// 建立新的bitmap，其内容是对原bitmap的缩放后的图
		Bitmap newbmp = Bitmap.createBitmap(oldbmp, 0, 0, width, height,
				matrix, true);
		return new BitmapDrawable(newbmp);
	}

	/**
	 * 将Drawable转化为Bitmap
	 * 
	 * @param drawable
	 * @return
	 */
	public static Bitmap drawableToBitmap(Drawable drawable) {
		// 取 drawable 的长宽
		int w = drawable.getIntrinsicWidth();
		int h = drawable.getIntrinsicHeight();

		// 取 drawable 的颜色格式
		Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
				: Bitmap.Config.RGB_565;
		// 建立对应 bitmap
		Bitmap bitmap = Bitmap.createBitmap(w, h, config);
		// 建立对应 bitmap 的画布
		Canvas canvas = new Canvas(bitmap);
		drawable.setBounds(0, 0, w, h);
		// 把 drawable 内容画到画布中
		drawable.draw(canvas);
		return bitmap;
	}

	/**
	 * Bitmap缩放,固定尺寸
	 * 
	 * @param bitmap
	 * @param width
	 * @param height
	 * @return
	 */
	@SuppressLint("NewApi")
	public static Bitmap zoomBitmap(Bitmap bitmap) {
		int scalew = 0, scaleh = 0;
		float width = bitmap.getWidth();
		float height = bitmap.getHeight();
		float defaultwh = DeviceHelper.getFitWidth(150);
		Logger.log(TAG, "zoomBitmap", width + "/" + height + "/" + defaultwh);
		if (width < defaultwh && height < defaultwh) {
			return bitmap;
		}
		if (width > defaultwh || height > defaultwh) {
			int rate1 = (int) (width / defaultwh);
			int rate2 = (int) (height / defaultwh);
			int rate = rate1 > rate2 ? rate1 : rate2;
			scalew = (int) (width / rate);
			scaleh = (int) (height / rate);
		}
		Logger.log(TAG, "zoomBitmap~", scalew + "/" + scaleh);
		if (scalew <= 0 || scalew > DeviceHelper.getFitWidth(150)) {
			scalew = DeviceHelper.getFitWidth(150);
		}
		if (scaleh <= 0 || scaleh > DeviceHelper.getFitHeight(150)) {
			scaleh = DeviceHelper.getFitHeight(150);
		}
		Bitmap newbmp = Bitmap
				.createScaledBitmap(bitmap, scalew, scaleh, false);
		// 小于4.1版本
		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
			bitmap.recycle();
		}
		return newbmp;
	}

	// 将byte[]转换成InputStream
	public static InputStream byte2InputStream(byte[] b) {
		ByteArrayInputStream bais = new ByteArrayInputStream(b);
		return bais;
	}

	// 将InputStream转换成byte[]
	public static byte[] inputStream2Bytes(InputStream is) {
		String str = "";
		byte[] readByte = new byte[1024];
		int readCount = -1;
		try {
			while ((readCount = is.read(readByte, 0, 1024)) != -1) {
				str += new String(readByte, 0, readCount).trim();
			}
			return str.getBytes();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	// 将Bitmap转换成InputStream
	public static InputStream bitmap2InputStream(Bitmap bm) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
		InputStream is = new ByteArrayInputStream(baos.toByteArray());
		return is;
	}

	// 将Bitmap转换成InputStream
	public static InputStream bitmap2InputStream(Bitmap bm, int quality) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		bm.compress(Bitmap.CompressFormat.PNG, quality, baos);
		InputStream is = new ByteArrayInputStream(baos.toByteArray());
		return is;
	}

	// 将InputStream转换成Bitmap
	public static Bitmap inputStream2Bitmap(InputStream is) {
		return BitmapFactory.decodeStream(is);
	}

	// Drawable转换成InputStream
	public static InputStream drawable2InputStream(Drawable d) {
		Bitmap bitmap = drawable2Bitmap(d);
		return bitmap2InputStream(bitmap);
	}

	// InputStream转换成Drawable
	public static Drawable inputStream2Drawable(InputStream is) {
		Bitmap bitmap = inputStream2Bitmap(is);
		return bitmap2Drawable(bitmap);
	}

	// Drawable转换成byte[]
	public static byte[] drawable2Bytes(Drawable d) {
		Bitmap bitmap = drawable2Bitmap(d);
		return bitmap2Bytes(bitmap);
	}

	// byte[]转换成Drawable
	public static Drawable bytes2Drawable(byte[] b) {
		Bitmap bitmap = bytes2Bitmap(b);
		return bitmap2Drawable(bitmap);
	}

	// Bitmap转换成byte[]
	public static byte[] bitmap2Bytes(Bitmap bm) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
		return baos.toByteArray();
	}

	// byte[]转换成Bitmap
	public static Bitmap bytes2Bitmap(byte[] b) {
		if (b.length != 0) {
			return BitmapFactory.decodeByteArray(b, 0, b.length);
		}
		return null;
	}

	// Drawable转换成Bitmap
	public static Bitmap drawable2Bitmap(Drawable drawable) {
		Bitmap bitmap = Bitmap
				.createBitmap(
						drawable.getIntrinsicWidth(),
						drawable.getIntrinsicHeight(),
						drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
								: Bitmap.Config.RGB_565);
		Canvas canvas = new Canvas(bitmap);
		drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
				drawable.getIntrinsicHeight());
		drawable.draw(canvas);
		return bitmap;
	}

	// Bitmap转换成Drawable
	@SuppressWarnings("deprecation")
	public static Drawable bitmap2Drawable(Bitmap bitmap) {
		BitmapDrawable bd = new BitmapDrawable(bitmap);
		Drawable d = (Drawable) bd;
		return d;
	}

	/**
	 * bitmap→file
	 * 
	 * @param bitmap
	 * @return
	 */
	public static String bitmapToFile(Bitmap bitmap, String filePath) {
		File newFile = new File(filePath);
		if (!newFile.exists()) {
			try {
				newFile.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
				return null;
			}
		}
		Logger.log(TAG, "bitmapToFile", newFile.getAbsolutePath());
		FileOutputStream fStream = null;
		try {
			fStream = new FileOutputStream(newFile);
			bitmap.compress(Bitmap.CompressFormat.PNG, 100, fStream);
			fStream.flush();
		} catch (IOException e) {
			e.printStackTrace();
			Logger.log(TAG, "bitmapToFile error", e);
			newFile.delete();
			newFile = null;
			return null;
		} finally {
			try {
				bitmap.recycle();
				if (fStream != null) {
					fStream.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
				return null;
			}
		}
		return newFile.getAbsolutePath();
	}

	/**
	 * 根据路径获取图片并压缩
	 * 
	 * @param imgFile
	 * @param compress
	 * @return
	 * @throws Exception
	 */
	public static Bitmap compressBitmap(String imgPath, int compress)
			throws Exception {
		BitmapFactory.Options newOpts = new BitmapFactory.Options();
		// 开始读入图片，此时把options.inJustDecodeBounds 设回true了
		newOpts.inJustDecodeBounds = true;
		Bitmap bitmap = BitmapFactory.decodeFile(imgPath, newOpts);// 此时返回bm为空
		newOpts.inJustDecodeBounds = false;

		int w = newOpts.outWidth;
		int h = newOpts.outHeight;
		// 现在主流手机比较多是800*480分辨率，所以高和宽我们设置为
		float currH = DeviceHelper.CURR_HEIGHT;// 当前设备高度
		float currW = DeviceHelper.CURR_WIDTH;// 当前设备宽度
		Logger.log(TAG, "compressBitmap", currW, currH);
		Logger.log(TAG, "compressBitmap~", newOpts.outWidth, newOpts.outHeight);
		// 缩放比。由于是固定比例缩放，只用高或者宽其中一个数据进行计算即可
		int be = 1;// be=1表示不缩放
		if (w > h && w > currW) {// 如果宽度大的话根据宽度固定大小缩放
			be = (int) (newOpts.outWidth / currW);
		} else if (w < h && h > currH) {// 如果高度高的话根据宽度固定大小缩放
			be = (int) (newOpts.outHeight / currH);
		}
		if (be <= 0)
			be = 1;
		newOpts.inSampleSize = be;// 设置缩放比例
		newOpts.inPreferredConfig = Config.RGB_565;
		// 重新读入图片，注意此时已经把options.inJustDecodeBounds 设回false了
		bitmap = BitmapFactory.decodeFile(imgPath, newOpts);
		return compressBitmap(bitmap, compress);// 压缩好比例大小后再进行质量压缩
	}

	/**
	 * 质量压缩图片
	 * 
	 * @param bitmap
	 * @param compress
	 * @return
	 */
	public static Bitmap compressBitmap(Bitmap bitmap, int compress) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		bitmap.compress(Bitmap.CompressFormat.PNG, compress, baos);// 质量压缩方法，这里100表示不压缩，把压缩后的数据存放到baos中
		Logger.log(TAG, "compressBitmap", baos.size());
		if (compress == 0) {// 默认压缩
			int options = 100;
			while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
				baos.reset();// 重置baos即清空baos
				bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%，把压缩后的数据存放到baos中
				options -= compress;// 每次都减少10
			}
		}
		Logger.log(TAG, "compressBitmap~", baos.size());
		ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
		Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
		return newBitmap;
	}

	/**
	 * 自定义缩放图片
	 * 
	 * @param bitmap
	 * @param newWidth
	 * @param newHeight
	 * @return
	 */
	@SuppressLint("NewApi")
	public static Bitmap zoomBitmap(Bitmap bitmap, float newWidth,
			float newHeight) {
		Logger.log(TAG, "zoomBitmap", newWidth, newHeight);
		// 获得图片的宽高
		int width = bitmap.getWidth();
		int height = bitmap.getHeight();
		// // 计算缩放比例
		float scaleWidth = ((float) newWidth) / width;
		float scaleHeight = ((float) newHeight) / height;
		if (scaleHeight > scaleWidth) {
			scaleHeight = scaleWidth;
		} else {
			scaleWidth = scaleHeight;
		}
		// 取得想要缩放的matrix参数
		Matrix matrix = new Matrix();
		matrix.postScale(scaleWidth, scaleHeight);
		// 得到新的图片
		Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height,
				matrix, true);
		// 小于4.1版本
		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
			bitmap.recycle();
		}
		Logger.log(TAG, "zoomBitmap~", newbmp.getWidth(), newbmp.getHeight());
		return newbmp;
	}

	public static int computeSampleSize(BitmapFactory.Options options,
			int minSideLength, int maxNumOfPixels) {
		int initialSize = computeInitialSampleSize(options, minSideLength,
				maxNumOfPixels);

		int roundedSize;
		if (initialSize <= 8) {
			roundedSize = 1;
			while (roundedSize < initialSize) {
				roundedSize <<= 1;
			}
		} else {
			roundedSize = (initialSize + 7) / 8 * 8;
		}

		return roundedSize;
	}

	private static int computeInitialSampleSize(BitmapFactory.Options options,
			int minSideLength, int maxNumOfPixels) {
		double w = options.outWidth;
		double h = options.outHeight;

		int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math
				.sqrt(w * h / maxNumOfPixels));
		int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(
				Math.floor(w / minSideLength), Math.floor(h / minSideLength));

		if (upperBound < lowerBound) {
			// return the larger one when there is no overlapping zone.
			return lowerBound;
		}

		if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
			return 1;
		} else if (minSideLength == -1) {
			return lowerBound;
		} else {
			return upperBound;
		}
	}

	/**
	 * 计算标准缩放倍数
	 * 
	 * @param options
	 * @return
	 */
	public static int calculateInSampleSize(BitmapFactory.Options options) {
		// Raw height and width of image
		int inSampleSize = 1;
		final int width = options.outWidth;
		final int height = options.outHeight;
		Logger.log(TAG, "calculateInSampleSize", width + "/ " + height);
		if (height > DeviceHelper.CURR_HEIGHT
				|| width > DeviceHelper.CURR_WIDTH) {
			// Calculate ratios of height and width to requested height and
			// width
			final int heightRatio = Math.round((float) height
					/ (float) DeviceHelper.CURR_HEIGHT);
			final int widthRatio = Math.round((float) width
					/ (float) DeviceHelper.CURR_WIDTH);
			inSampleSize = heightRatio < widthRatio ? widthRatio : heightRatio;
		}
		return inSampleSize;
	}

	/**
	 * 旋转图片
	 */
	public static Bitmap rotateBitmap(Bitmap bitmap, int rotate) {
		if (bitmap == null)
			return null;

		int w = bitmap.getWidth();
		int h = bitmap.getHeight();

		Matrix mtx = new Matrix();
		mtx.postRotate(rotate);
		return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
	}

	/**
	 * 
	 * @param inStream
	 * @return
	 * @throws Exception
	 */
	public static byte[] streamToBytes(InputStream iStream) throws Exception {
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = 0;
		while ((len = iStream.read(buffer)) != -1) {
			outStream.write(buffer, 0, len);
		}
		outStream.close();
		iStream.close();
		return outStream.toByteArray();
	}

	/**
	 * 根据路径加载bitmap
	 * 
	 * @param path
	 *            路径
	 * @param w
	 *            款
	 * @param h
	 *            长
	 * @return
	 */
	public static Bitmap fileToBitmap(String filePath, int w, int h) {
		try {
			BitmapFactory.Options opts = new BitmapFactory.Options();
			// 设置为ture只获取图片大小
			opts.inJustDecodeBounds = true;
			opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
			// 返回为空
			BitmapFactory.decodeFile(filePath, opts);
			int width = opts.outWidth;
			int height = opts.outHeight;
			if (w==0) {
				w=DeviceHelper.CURR_WIDTH;
				h=DeviceHelper.CURR_HEIGHT;
			}
			float scaleWidth = 0.f, scaleHeight = 0.f;
			if (width > w || height > h) {
				// 缩放
				scaleWidth = ((float) width) / w;
				scaleHeight = ((float) height) / h;
			}
			opts.inJustDecodeBounds = false;
			float scale = Math.max(scaleWidth, scaleHeight);
			opts.inSampleSize = (int) scale;
			WeakReference<Bitmap> weak = new WeakReference<Bitmap>(
					BitmapFactory.decodeFile(filePath, opts));
			Bitmap bMapRotate = Bitmap.createBitmap(weak.get(), 0, 0, weak
					.get().getWidth(), weak.get().getHeight(), null, true);
			return bMapRotate;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 本地加载
	 * 
	 * @param view
	 * @param imgPath
	 * @param defaultRes
	 */
	public static void loadFileByImgloader(ImageView view, String imgPath,
			int defaultRes) {
		loadFileByImgloader(null, null, view, imgPath, defaultRes);
	}

	/**
	 * 带监听本地加载
	 * 
	 * @param loadingListener
	 * @param progressListener
	 * @param view
	 * @param imgPath
	 * @param defaultRes
	 */
	public static void loadFileByImgloader(ImageLoadingListener loadingListener,
			ImageLoadingProgressListener progressListener, ImageView view,
			String imgPath, int defaultRes) {
		if (!StringHelper.hasValue(imgPath)) {
			if (loadingListener != null) {
				loadingListener.onLoadingFailed(imgPath, view, null);
			}
			if (progressListener != null) {
				progressListener.onProgressUpdate(imgPath, view, -1, -1);
			}
			return;
		}
		if (!imgPath.startsWith("file://")) {
			imgPath = "file://" + imgPath;
		}
		ImageLoader.getInstance().displayImage(imgPath, view,
				getDisplayOptions(true, defaultRes), loadingListener,
				progressListener);
	}

	/**
	 * 网络加载
	 * 
	 * @param view
	 * @param imgLink
	 * @param defaultRes
	 */
	public static void loadLinkByImgloader(ImageView view, String imgLink,
			int defaultRes) {
		String filePath = FileHelper.getDiskCachePath(FileHelper.IMAGE_CACHE_DIR);
		File imgFile = new File(filePath, imgLink.hashCode() + ".png");
		Logger.log(TAG, "loadImgByLink", imgFile.getAbsolutePath(),
				imgFile.exists());
		if (imgFile.exists()) {
			loadFileByImgloader(view, imgFile.getAbsolutePath(), defaultRes);
		} else {
			loadLinkByImgloader(null, null, view, imgLink, imgFile.getAbsolutePath(),
					defaultRes);
		}
	}

	/**
	 * 带监听网络加载
	 * 
	 * @param loadingListener
	 * @param progressListener
	 * @param view
	 * @param imgLink
	 * @param filePath
	 * @param defaultRes
	 */
	public static void loadLinkByImgloader(ImageLoadingListener loadingListener,
			ImageLoadingProgressListener progressListener, ImageView view,
			String imgLink, int defaultRes) {
		if (!StringHelper.hasValue(imgLink)) {
			if (loadingListener != null) {
				loadingListener.onLoadingFailed(imgLink, view, null);
			}
			if (progressListener != null) {
				progressListener.onProgressUpdate(imgLink, view, -1, -1);
			}
			return;
		}
		if (!imgLink.startsWith("http://")) {
			imgLink = "http://" + imgLink;
		}
		String filePath = FileHelper.getDiskCachePath(FileHelper.IMAGE_CACHE_DIR);
		File imgFile = new File(filePath, imgLink.hashCode() + ".png");
		if (imgFile.exists()) {
			loadFileByImgloader(view, imgFile.getAbsolutePath(), defaultRes);
		} else {
			loadLinkByImgloader(null, null, view, imgLink, imgFile.getAbsolutePath(),
					defaultRes);
		}
	}

	/**
	 * 带监听网络加载
	 * 
	 * @param loadingListener
	 * @param progressListener
	 * @param view
	 * @param imgLink
	 * @param filePath
	 * @param defaultRes
	 */
	public static void loadLinkByImgloader(ImageLoadingListener loadingListener,
			ImageLoadingProgressListener progressListener, ImageView view,
			String imgLink, String filePath, int defaultRes) {
		Logger.log(TAG, "loadImgByLink", imgLink, filePath);
		if (!StringHelper.hasValue(imgLink)) {
			if (loadingListener != null) {
				loadingListener.onLoadingFailed(imgLink, view, null);
			}
			return;
		}
		if (!imgLink.startsWith("http://")) {
			imgLink = "http://" + imgLink;
		}
		ImageLoader.getInstance().displayImage(imgLink, view,
				getDisplayOptions(true, defaultRes), loadingListener,
				progressListener);
	}

}
